package com.mackyhuang.annotated.enhancer.enhancer;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import com.mackyhuang.annotated.enhancer.model.EnhanceEvent;
import com.mackyhuang.annotated.enhancer.proxy.ProxyFactory;
import com.mackyhuang.annotated.enums.EnhancementEnum;

/**
 * @description:
 * @author: mackyHuang
 * @create: 2021/01/02
 */
public class Enhancement implements InvocationHandler, net.sf.cglib.proxy.InvocationHandler {

    private Object target;

    private Class annotation;

    private EnhanceAdapter adapter;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Annotation hasAnnotation = method.getAnnotation(this.annotation);

        if (hasAnnotation == null){
            return method.invoke(target, args);
        }
        EnhanceEvent enhanceEvent = new EnhanceEvent(target, method, args);

        if (adapter.preInvoke(enhanceEvent)){
            return enhanceEvent.getResult();
        }

        try {
            Object result = method.invoke(target, args);
            enhanceEvent.setResult(result);
            adapter.postInvoke(enhanceEvent);
            return enhanceEvent.getResult();
        } catch (Exception e){
            enhanceEvent.setException(e);
            adapter.postThrowing(enhanceEvent);
            return enhanceEvent.getResult();
        } finally {
            adapter.postReturning(enhanceEvent);
        }
    }

    public Object doProxy(Object target, EnhanceAdapter adapter, Class annotation){
        this.target = target;
        this.adapter = adapter;
        this.annotation = annotation;
        Object result;
        ProxyFactory instance = ProxyFactory.getInstance();
        if (!Modifier.isFinal(target.getClass().getModifiers())){
            result = instance.getProxy(EnhancementEnum.CGLIB_PROXY.getCode());
        } else {
            result = instance.getProxy(EnhancementEnum.JDK_PROXY.getCode());
        }
        return result;
    }

}
